#include "BF.h"
#include <string.h>

int StoreFile ( FILE *file, char *fileName , int index );
bool ExistsInHeaders ( char *fileName , int *headerIndex );
void IncreasePointer (char *fileName );
void SetPointer ( char *fileName );
void RemovePointer ( char *fileName );
bool Disposed ( char *fileName );
int FindHeader ( char *fileName );
void DeleteHeader ( char *fileName );
int FindNewHeaderentry ( int *headerIndex );


void FMM_Init ( void )
{
	int i;

	for ( i = 0; i < MAXOPENFILES; i++)
	{
		openFiles[i].freeEntry = true;
		filePointers[i].pointers = 0;
		headers[i].freeEntry = true;
		memset(headers[i].buffer, 0, sizeof(headers[i].buffer));
	}
}



int FMM_Open ( char* fileName )
{
	FILE *file;
	int result, i, entry;
	bool foundEmptyEntry = false;

	/*...... ... .. ....... ........ ..... .... ......*/

	for ( i = 0; i < MAXOPENFILES; i++)
	{
		if ( openFiles[i].freeEntry == true)
		{
			foundEmptyEntry = true;
			break;
		}
	}

	/* .. ......., ......... .. .......
	 *...... ............ ...... .......
	 */

	if ( foundEmptyEntry )
	{
		result = HDF_OpenFile ( fileName, &file );
		
		if ( result < 0 )
		{
			return result;
		}
	}
	else
	{
		/*.. ... ....... ......... ..... .... ...... openFiles
		 *......... .......... error.
		 */

		BF_errno = BFE_NOBUF;
		return BFE_NOBUF;
	}

	/*.......... ... ....... .... ..... ... FMM.*/
	return StoreFile ( file, fileName, i );
}


int FMM_Close ( int fileDesc )
{
	FILE *file;
	int headerDesc, result;
	char fileName[MAX_FILE_NAME];

	/* ...... ... ... file descriptor, .. .....
	 *..... ......... error.
	 */

	if ( FMM_IsValid ( fileDesc ) )
	{
		BF_errno = BFE_INVALIDBLOCK;
		return BFE_INVALIDBLOCK;
	}

	file = openFiles[fileDesc].fp;
	headerDesc = openFiles[fileDesc].headerDesc;

	//........ ... ........... ... ....... ... ... ...... ....... ........*/

	openFiles[fileDesc].freeEntry = true;

	/*...... ... ...... ... ....... ..... ..... .... ........ .. ....
	 *...... ... .. .... . .........., ..... ... ... .......... ...
	 *......... ... .... .... ... ...... .. headers.
	 */

	RemovePointer ( fileName );
	if ( Disposed ( fileName ) )
	{
		DeleteHeader ( fileName ); 

		/*....... ... .. .. Header .... ........ .. ...
		*..... ... ........ ... ......
		 */
	
		if ( headers[headerDesc].dirty )
		{
			result = HDF_WriteBlock ( 0, headers[headerDesc].buffer, file);
		}
	}

	return HDF_CloseFile ( file );
}

bool FMM_IsOpen ( char* fileName )
{
	int i;

	for ( i = 0; i < MAXOPENFILES; i++ )
	{
		if ( strcmp (fileName, headers[i].fileName ) == 0 )
		{
			if ( headers[i].freeEntry == false )
			{
				return true;
			}
		}
	}

	return false;
}

bool FMM_IsValid ( int fileDesc )
{
	int i, result;
	FileInfo_t fileInfo;

	if ( fileDesc < 0 || fileDesc >= MAXOPENFILES )
	{
		return false;
	}
	
	return openFiles[fileDesc].freeEntry;
}

int FMM_GetFileInfoByName ( FileInfo_t* finfo )
{
	int fileDesc, headerDesc;
	char *fileName[MAX_FILE_NAME];
	

	for ( headerDesc = 0; headerDesc < MAXOPENFILES; headerDesc++ )
	{
		if ( headers[headerDesc].freeEntry == true && strcmp (fileName, headers[headerDesc].fileName) == 0 )
		{
			for ( fileDesc = 0; fileDesc < MAXOPENFILES; fileDesc++ )
			{
				if ( openFiles[fileDesc].headerDesc == headerDesc)
				{
					finfo->fileDesc = fileDesc;
					finfo->fp = openFiles[fileDesc].fp;

					BF_errno = BFE_OK;
					return BFE_OK;
				}
			}
		}
	}

	BF_errno = BFE_FILENOTEXISTS;
	return BFE_FILENOTEXISTS;
}



int FMM_GetFileInfoByFD ( FileInfo_t* finfo )
{
	int i, fileDesc, headerDesc;
	char fileName[MAX_FILE_NAME];
	
	/* '....... ... ....... file descriptor.*/

	if ( FMM_IsValid ( finfo->fileDesc ) )
	{
		BF_errno = BFE_FD;
		return BFE_FD;
	}

	fileDesc = finfo->fileDesc;
	headerDesc = openFiles[fileDesc].headerDesc;

	finfo->fp = openFiles[fileDesc].fp;
	strcpy ( finfo->filename, headers[headerDesc].fileName );

	return BFE_OK;
}

bool FMM_HEAD_IsValid ( int fileDesc, int blockNum )
{
	int targetBit, headerDesc;
	unsigned int byte, mask, result;

	if ( FMM_IsValid ( fileDesc ) )
	{
		BF_errno = BFE_FD;
		return BFE_FD;
	}

	/* mask == 10000000 */
	mask = 128;

	headerDesc = openFiles[fileDesc].headerDesc;

	/* ..... .. byte ... ... ........ ... ..... ......... .. bit 
	 *... blockNum.
	 */

	byte = headers[headerDesc].buffer[blockNum / 8];
	targetBit = blockNum % 8;

	/* ........ ... .... ... .... ... ........ .. bit ... byte.*/
	
	mask >>= targetBit;

	/* ...... AND ... byte ... ... ..... ... .. .. ..........
	 *... ..... 00000000 ........ ... .. bit ..... 0.
	 */
	
	result = byte & mask;

	return result != 0? true : false;
}
int FMM_HEAD_GetFirstBlock ( int fileDesc )
{
	int i = 0;

	/* .. . fileDescripton ..... ..... ......... error.*/

	if ( FMM_IsValid ( fileDesc ) )
	{
		BF_errno = BFE_INVALIDBLOCK;
		return BFE_INVALIDBLOCK;
	}

	/*.......... ... .. 1. block ... header, .....
	 *.. ..... ....... .. ... ..... ...... block ......
	 *......... NOBLOCKDFOUND error.
	 */

	while ( !FMM_HEAD_IsValid ( fileDesc, i) )
	{
		i++;

		if ( i > MAX_BLOCKS )
		{
			BF_errno = BFE_EOF;
			return BFE_EOF;
		}
	}

	return i;
}

int FMM_HEAD_GetFirstEmpty ( int fileDesc )
{
	int i = 0;

	/* .. . fileDescripton ..... ..... ......... error.*/

	if ( FMM_IsValid (fileDesc ) )
	{
		BF_errno = BFE_INVALIDBLOCK;
		return BFE_INVALIDBLOCK;
	}

	/*.......... ... .. 1. block ... header, .....
	 *.. ..... ...... .. ... ..... ...... block ......
	 *......... NOBLOCKDFOUND error.
	 */

	while ( FMM_HEAD_IsValid ( fileDesc, i) )
	{
		i++;

		if ( i > MAX_BLOCKS )
		{
			BF_errno = BFE_EOF;
			return BFE_EOF;
		}
	}

	return i;
	
}

int FMM_HEAD_Set ( int fileDesc, int blockNum, bool value)
{
	int targetBit, headerDesc;
	unsigned int  byte, mask, result;

	headerDesc = openFiles[fileDesc].headerDesc;

	/* ..... .. byte ... ......... .. bit ... blockNum block
	 * ... ..... .. bit ... .....
	 */

	byte = headers[headerDesc].buffer[blockNum / 8];
	targetBit = blockNum % 8;


	/* mask == 10000000 */
	mask = 128;
	
	/*........ ... .... ... .... ... ......... .. bit ... byte.*/
	mask >>= targetBit;

	/* .. ...... .. ..... 1 .. bit, .... ...... OR .. ... .....
	 *.... .. ........... ... .... ....., ..... ... .. targetBit 
	 *... .. ..... 1.
	 * .. ...... .. ..... 0, ......... ... ..... ... .... ...... AND
	 * .... .. ........... ... .... ..... ... .. targetBit ... .. ..... 0.
	 */

	if ( value == true )
	{
		// ie: byte == 11001100
		// targetBit == 5
		// mask == 00010000
		// byte | mask == 11011100
		byte = byte | mask;
	}
	else
	{
		// ie: byte == 1000000
		// targetBit == 5
		// mask == 01000000
		// ~mask == 10111111
		// byte & mask == 1000000

		mask = ~mask;
		byte = byte & mask;
	}

	headers[headerDesc].buffer[ blockNum / 8] = (char)byte;
	headers[headerDesc].dirty = true;
}
int FMM_HEAD_GetNextBlock ( int fileDesc, int prev )
{
	int i;

	/* .. . fileDescripton ..... ..... ......... error.*/
	if ( fileDesc < 0 || fileDesc >= MAXOPENFILES)
	{
		BF_errno = BFE_INVALIDBLOCK;
		return BFE_INVALIDBLOCK;
	}

	/*.......... ... .. prev + 1 block ... header, .....
	 *.. ..... ....... .. ... ..... ...... block ......
	 *......... NOBLOCKDFOUND error.
	 */

	i = prev + 1;
	while ( !FMM_HEAD_IsValid ( fileDesc, i ) )
	{
		i++;

		if ( i > MAX_BLOCKS )
		{
			BF_errno = BFE_EOF;
			return BFE_EOF;
		}
	}

	return i;

}
/*************************************************************/
int StoreFile ( FILE *file, char *fileName , int fileDesc)
{
	/*.......... ........... ... ....... ... .... openFiles.*/

	openFiles[fileDesc].fp = file;
	openFiles[fileDesc].freeEntry = false;

	if ( FMM_IsOpen ( fileName ) )
	{
		IncreasePointer ( fileName );
	}
	else
	{
		SetPointer ( fileName ); 
	}

	/*.. . ........ ... ....... ... ....... ... ... .... ... 
	 *....... ... ........., ... ..........
	 */

	if ( !ExistsInHeaders ( fileName , &( openFiles[fileDesc].headerDesc) ) )
	{
		/*...... .... ..... ..........*/

		int headerDesc = FindNewHeaderentry ( fileName );

		/*.......... ........... ... ... .........*/
	
		HDF_ReadBlock ( 0, headers[headerDesc].buffer, file );
		strcpy ( headers[headerDesc].fileName, fileName );
		headers[headerDesc].freeEntry = false;
		headers[headerDesc].dirty = false;

		openFiles[fileDesc].headerDesc = headerDesc;
	}

	return fileDesc;
}

void IncreasePointer (char *fileName )
{
	int i;

	for ( i = 0; i < MAXOPENFILES; i++ )
	{
		if ( strcmp(filePointers[i].fileName, fileName ) == 0 )
		{
			filePointers[i].pointers++;
		}
	}
}

void SetPointer ( char *fileName )
{
	int i = 0;

	while ( filePointers[i].pointers != 0 )
		i++;

	filePointers[i].pointers++;
	strcpy ( filePointers[i].fileName, fileName );
}

void RemovePointer ( char *fileName )
{
	int i;

	for ( i = 0; i < MAXOPENFILES; i++ )
	{
		if ( strcmp ( filePointers[i].fileName, fileName ) == 0 )
		{
			filePointers[i].pointers--;
			return;
		}
	}
}
int FindHeader ( char *fileName )
{
	int index;
	
	for ( index = 0; index < MAXOPENFILES; index++ )
	{
		if ( strcmp ( fileName, headers[index].fileName ) == 0 )
			return index;
	}
	
	return false;
}

bool ExistsInHeaders ( char *fileName , int *headerIndex ) 
{
	int i;

	for ( i = 0; i < MAXOPENFILES; i++)
	{
		if ( strcmp( headers[i].fileName, fileName ) == 0 )
		{
			*headerIndex = i;
			return true;
		}
	}

	return false;
}

bool Disposed ( char *fileName ) 
{
	int i;

	for ( i = 0; i < MAXOPENFILES; i++ )
	{
		if ( strcmp ( filePointers[i].fileName, fileName ) == 0 )
		{
			if ( filePointers[i].pointers == 0 )
			{
				return true;
			}
			else
			{
				return false;
			}
		}
	}
}

void DeleteHeader ( char *fileName )
{
	int i;

	for ( i = 0; i < MAXOPENFILES; i++ )
	{
		if ( strcpy ( headers[i].fileName, fileName ) == 0 )
		{
			headers[i].freeEntry = true;
			return;
		}
	}
}

int FindNewHeaderentry ( int *headerIndex )
{
	int i;

	for ( i = 0; i < MAXOPENFILES; i++ )
	{
		if ( headers[i].freeEntry == true )
		{
			return i;
		}
	}
}
